// $Id: testAliHLTFileWriter.C 47755 2011-02-23 22:27:59Z richterm $

/**************************************************************************
 * This file is property of and copyright by the ALICE HLT Project        * 
 * ALICE Experiment at CERN, All rights reserved.                         *
 *                                                                        *
 * Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no>        *
 *                  for The ALICE HLT Project.                            *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/** @file   testAliHLTFileWriter.C
    @author Matthias Richter
    @date   
    @brief  Test macro/program for the AliHLTFileWriter
 */

#ifndef __CINT__
#include <ostream>
#include <vector>
#include "AliHLTComponent.h"
#include "AliHLTComponentHandler.h"
#include "AliHLTConfiguration.h"
#include "TString.h"
#include "TFile.h"
#include "TSystem.h"
#else
#define EINVAL 22
#define EPROTO 71
#endif //__CINT__

typedef AliHLTUInt32_t TestElement_t;
const char* testDirectory="/tmp/testAliHLTFileWriter";

int SetEvent(unsigned eventNo, vector<TestElement_t>& testData)
{
  unsigned shift=(sizeof(TestElement_t)-1)*8;
  AliHLTUInt32_t mask=0xf<<shift;
  for (unsigned i=0; i<testData.size(); i++) {
    testData[i]&=~mask;
    testData[i]|=eventNo<<shift;
  }
  return 0;
}

int TestCycle(AliHLTComponent* pComponent, const char* arguments, AliHLTComponentBlockDataList blocks,
	      vector<TestElement_t>& testData, unsigned int nofEvents=1)
{
  int iResult=0;
  vector<char*> argList;
  if ((iResult=AliHLTConfiguration::InterpreteString(arguments, argList))<0) {
    cerr << "failed: chopping arguments \"" << arguments << "\"" << endl;
  }

  // notepad for generalized unit test class
  // - test component for outputsize==0 if no blocks are added

  // init FileWriter component
  if ((iResult=pComponent->Init(NULL, NULL, argList.size(), const_cast<const char**>(&(argList[0]))))!=0) {
    cerr << "failed: initializing component" << endl;
    return iResult;
  }

  // process event
  AliHLTComponentBlockData eventTypeBlock;
  AliHLTComponent::FillBlockData(eventTypeBlock);
  eventTypeBlock.fDataType=kAliHLTDataTypeEvent;
  eventTypeBlock.fSpecification=gkAliEventTypeData;
  blocks.push_back(eventTypeBlock);

  AliHLTComponentEventData evtData;
  memset(&evtData, 0, sizeof(AliHLTComponentEventData));
  evtData.fStructSize=sizeof(AliHLTComponentEventData);
  evtData.fEventID=0;
  evtData.fBlockCnt=blocks.size();
  
  AliHLTComponentTriggerData trigData;
  memset(&trigData, 0, sizeof(AliHLTComponentTriggerData));
  trigData.fStructSize=sizeof(AliHLTComponentTriggerData);

  AliHLTUInt32_t size=0;
  AliHLTUInt32_t outputBlockCnt=0;
  AliHLTComponentBlockData* outputBlocks=NULL;
  AliHLTComponentEventDoneData* edd=NULL;

  for (unsigned eventNo=0; eventNo<nofEvents; eventNo++) {
    SetEvent(eventNo, testData);
    evtData.fEventID=eventNo;
    if ((iResult=pComponent->ProcessEvent(evtData, &(blocks[0]), trigData, NULL, size, outputBlockCnt, outputBlocks, edd))!=0) {
      cerr << "failed: event processing" << endl;
      break;
    }
  }
  blocks.pop_back();

  // cleanup FileWriter component
  if ((iResult=pComponent->Deinit())!=0) {
    cerr << "failed: deinit component" << endl;
  }

  // cleanup argument vector
  for (vector<char*>::iterator element=argList.begin();
       element!=argList.end(); element++) {
    delete *element;
  }
  argList.clear();

  return iResult;
}

int BuildFileName(unsigned eventNo, unsigned blockID,
		  const AliHLTComponentDataType& dataType,
		  const AliHLTUInt32_t /*specification*/,
		  bool bConcatenateEvents, bool bConcatenateBlocks,
		  TString& filename)
{
  int iResult=0;
  filename="";

  filename+=testDirectory;
  if (!filename.EndsWith("/")) {
    filename+="/";
  }

  filename+="event";
  if (!bConcatenateEvents) {
    filename+=Form("_0x%08x", eventNo);
  }
  if (!bConcatenateBlocks && !bConcatenateEvents) {
    filename+=Form("_0x%02x", blockID);
    if (dataType!=kAliHLTVoidDataType) {
      filename+="_";
      filename+=AliHLTComponent::DataType2Text(dataType).data();
    }
    //filename+=Form("", specification);
  }
  return iResult;
}

int CheckOutputFiles(AliHLTComponentBlockDataList blocks, vector<TestElement_t>& testData, 
		     bool bConcatenateEvents, bool bConcatenateBlocks,
		     unsigned int nofEvents=1)
{
  int iResult=0;
  AliHLTUInt32_t fileOffset=0;
  TString lastFileName;
  TFile* pFile=NULL;
  TArrayC buffer;
  for (unsigned eventNo=0; eventNo<nofEvents && iResult>=0; eventNo++) {
    SetEvent(eventNo, testData);
    for (unsigned blockNo=0; blockNo<blocks.size(); blockNo++) {
      TString fileName;
      BuildFileName(eventNo, blockNo, blocks[blockNo].fDataType, blocks[blockNo].fSpecification, bConcatenateEvents, bConcatenateBlocks, fileName);
      if (pFile && !fileName.IsNull() && fileName!=lastFileName) {
	pFile->Close();
	delete pFile;
	pFile=NULL;
	fileOffset=0;
      }
      lastFileName=fileName;
      fileName+="?filetype=raw";
      if (!pFile) pFile=new TFile(fileName);
      if (!pFile || pFile->IsZombie()) {
	cerr << "failed: can not open file " << fileName.ReplaceAll("?filetype=raw", "") << endl;
	iResult=-ENOENT;
	break;
      }
      if (buffer.GetSize()<(int)blocks[blockNo].fSize) {
	buffer.Set(blocks[blockNo].fSize);
      }
      if ((pFile->ReadBuffer((char*)buffer.GetArray(), blocks[blockNo].fSize))!=0) {
	cerr << "failed: reading " << blocks[blockNo].fSize << " at offset " << fileOffset << " from file " << fileName.ReplaceAll("?filetype=raw", "") << endl;
	iResult=-EIO;
	break;
      }
      AliHLTUInt8_t* origin=(AliHLTUInt8_t*)blocks[blockNo].fPtr;
      AliHLTUInt8_t* processed=(AliHLTUInt8_t*)buffer.GetArray();
      if (memcmp(origin, processed, blocks[blockNo].fSize)!=0) {
	cerr << "failed: data missmatch in event " << eventNo << " block " << blockNo << endl;
	iResult=-EFAULT;
	break;
      }
      cout << "checked: file " << fileName.ReplaceAll("?filetype=raw", "") << " offset " << fileOffset << endl;
      fileOffset+=blocks[blockNo].fSize;
    }
  }
  if (pFile) {
    pFile->Close();
    delete pFile;
    pFile=NULL;
  }
  return iResult;
}

int testAliHLTFileWriter()
{
  int iResult=0;
  AliHLTComponentHandler chandler;
  if ((iResult=chandler.LoadLibrary("libAliHLTUtil.so"))<0) {
    cerr << "failed: loading libAliHLTUtil" << endl;
    return iResult;
  }

  if ((iResult=chandler.LoadLibrary("libHLTrec.so"))<0) {
    cerr << "failed: loading libHLTrec" << endl;
    return iResult;
  }

  // create FileWriter component
  AliHLTComponent* pComponent=NULL;
  if ((iResult=chandler.CreateComponent("FileWriter", pComponent))<0) {
    cerr << "failed: can not create component \"FileWriter\"" << endl;
    return iResult;
  }

  AliHLTComponentBlockDataList blocks;
  const unsigned testDataSize=500;
  vector<TestElement_t> testData;
  testData.assign(testDataSize, 0);
  unsigned int i=0;
  for (; i<testDataSize; i++) testData[i]=i;

  const unsigned elementsPerBlock=100;
  AliHLTComponentBlockData bd;
  AliHLTComponent::FillBlockData(bd);
  for (i=0; i*elementsPerBlock<testData.size(); i++) {
    bd.fPtr=&(testData[i*elementsPerBlock]);
    bd.fSize=elementsPerBlock*sizeof(TestElement_t);
    bd.fSpecification=i;
    AliHLTComponent::SetDataType(bd.fDataType, "DUMMYDAT", "TEST");
    blocks.push_back(bd);
  }
  
  const char* testArguments[]={
    "",
    "-concatenate-blocks ",
    "-concatenate-blocks -concatenate-events ",
    // not currently working   "-burst-buffer 10000 ",
    // not currently working   "-burst-buffer 5000 ",
    // not currently working   "-burst-buffer 10000 -concatenate-blocks ",
    "-burst-buffer 10000 -concatenate-blocks -concatenate-events ",
    "-burst-buffer 5000 -concatenate-blocks -concatenate-events ",
    NULL
  };

  for (const char** currentTest=testArguments; *currentTest!=NULL; currentTest++) {
    TString arguments=*currentTest;
      arguments+=" -directory "; arguments+=testDirectory;
    if (!gSystem->AccessPathName(testDirectory)) {
      TString command="rm -r "; command+=testDirectory;
      gSystem->Exec(command);
    }
    cout << arguments.Data() << endl;
    if ((iResult=TestCycle(pComponent, arguments.Data(), blocks, testData, 4))<0) {
      break;
    }
    if ((iResult=CheckOutputFiles(blocks, testData, arguments.Contains("-concatenate-events "), arguments.Contains("-concatenate-blocks"), 4))<0) {
      break;
    }
  };

  delete pComponent;

  return iResult;
}

int main(int /*argc*/, const char** /*argv*/)
{
  int iResult=0;
  if ((iResult=testAliHLTFileWriter())<0) {
  }
  return iResult;
}
